Android 超炫酷的空气净化器控件 | 自定义控件
本文作者
作者:mochixuan
链接:
https://juejin.im/post/5a0904376fb9a044fe45da34
本文由作者授权发布。
最近把公司智能家具类的应用中的一个负离子净化显示的控件重写了,干脆就封装了一下起个高大上的名字空气净化器控件,感觉比负离子净化牛逼一点,好像最近锤子也发布了一个空气净化器。
来个动态的(Gif效果好差没办法它限制5M大小了,真实效果:扇叶是减速运动的),还是看后面吧...
刚开始写博客,没经验啊,看了下别人的都写原理什么的,这次就写一下里面相当比较复杂的效果的,其实没什么复杂的,不知道从何说起,于是乎捡几个用于的说。
1. 实现的功能 (注意以思路为主)
1.实现扇叶的渐变显示,更加真实
2. 背景颜色实现渐变切换
3. 实现扇叶无缝开启和关闭,从上次结束的位置开始动画
4. 实现颗粒物效果
5. 无缝改变扇叶的速度
2. 实现扇叶的渐变 (类似于真实扇叶的卷起来效果)
扇叶对比
简单说下这个的实现,实现右侧的圆块两行代码解决。
val pathEffect
= DashPathEffect(
floatArrayOf(mPaint.strokeWidth * 0.4f,
mPaint.strokeWidth), 0f)
mPaint.pathEffect = pathEffect
canvas.drawCircle(dashedRingCx, dashedRingCy, dashedRingRadius, mPaint)
但是 渐变的怎么绘制呢?上面的方法就不能用了,因为Android绘制渐变的我想到的只有LinearGradient,我说的是一个个绘制渐变,先说下我的方法,LinearGradient需要传入好几个参数其中这里比较重要的就是x0,y0,x1,y1,就是你需要从那个点渐变到那个点,直接再上个图标就是求出图中两个就可以绘制一个渐变的小圆环了,然后怎么绘制一个圆弧了。
//实现渐变扇叶
while (curAngle < 360 - mEachPanAngle) {
val x0 = measuredWidth / 2f + (Math.cos((curAngle) * Math.PI / 180) * (dashedRingRadius - dashedRadiusDiff * 0.5)).toFloat()
val y0 = measuredHeight / 2f + (Math.sin((curAngle) * Math.PI / 180) * (dashedRingRadius - dashedRadiusDiff * 0.5)).toFloat()
val x1 = measuredWidth / 2f + (Math.cos((curAngle + mEachPanAngle) * Math.PI / 180) * (dashedRingRadius + dashedRadiusDiff * 0.5)).toFloat()
val y1 = measuredHeight / 2f + (Math.sin((curAngle + mEachPanAngle) * Math.PI / 180) * (dashedRingRadius + dashedRadiusDiff * 0.5)).toFloat()
val shader = LinearGradient(x0, y0, x1, y1,
Color.parseColor("#22ffffff"),
Color.parseColor("#ffffffff"),
Shader.TileMode.CLAMP)
mPaint.shader = shader
canvas ?.drawArc(rectF, curAngle, mEachPanAngle, false, mPaint)
curAngle = curAngle + mEachPanAngle + mEachPanAngleGap
}
mPaint.shader = null //记得清除
这个的实现方法就很多了可以直接属性动画一个起始值一个结束值,设置animator.setEvaluator(ArgbEvaluator()),还有一个就是使用Hsv 使颜色渐变更适合人类观感,公式就不写,网上有源码里也有。
4. 实现扇叶无缝开启和关闭
这个动画还是使用属性动画,但注意每次开启和关闭时传入的值,当前值为起始值,结束值为你要到的值,代码其实很简单。
private fun onFanAnim(isOpenFan: Boolean) {
if (mJumpAnimator != null && mJumpAnimator!!.isRunning) {
mJumpAnimator!!.cancel()
}
if (isOpenFan) {
mJumpAnimator = ObjectAnimator.ofFloat(this,"dashedRadiusDiff",dashedRadiusDiff,mDashedRingWidth)
mJumpAnimator!!.interpolator = DecelerateInterpolator()
mJumpAnimator!!.addListener(object : Animator.AnimatorListener{
override fun onAnimationRepeat(p0: Animator?) {
}
override fun onAnimationEnd(p0: Animator?) {
if (!isCancelJumpAnim) {
mPanListener?.onHasOpen()
onRotateAnim()
}
isCancelJumpAnim = false
}
override fun onAnimationCancel(p0: Animator?) {
isCancelJumpAnim = true
}
override fun onAnimationStart(p0: Animator?) {
}
})
mJumpAnimator!!.duration = 2000
} else {
mJumpAnimator = ObjectAnimator.ofFloat(this,"dashedRadiusDiff",dashedRadiusDiff,0f)
mJumpAnimator!!.interpolator = AccelerateInterpolator()
mJumpAnimator!!.addListener(object : Animator.AnimatorListener{
override fun onAnimationRepeat(p0: Animator?) {
}
override fun onAnimationEnd(p0: Animator?) {
if (dashedRadiusDiff == 0f) {
mPanListener?.onHasClose()
}
}
override fun onAnimationCancel(p0: Animator?) {
}
override fun onAnimationStart(p0: Animator?) {
}
})
mJumpAnimator!!.duration = 1200
}
mJumpAnimator!!.start()
}
5. 实现颗粒物效果
1.实现颗粒物无方向飘动
2.顺时针向圆心运动就是实现被设备吸入效果
这个实现方法非常多,我实现方法应该是比较简单的,但效果不是特别符合空气效果,颗粒物实现了两个效果一个随机流动还有一个随着扇叶渐变到。
1.无方向浮动是每次刷新都进行加一个随机 -0.5 ~ 0.5数字
2.被设备吸入的效果使用角度增加,半径减小,是不是很简单。
6. 无缝改变扇叶的速度 (GIF效果不太好)
这个实现过程更简单了每次改变动画的时间但要记住起点是上一次动画结束点,终点是结束点+360。
更加详细的直接参考源码吧:
https://github.com/mochixuan/AirPurgeView
推荐阅读:
超详细Android面试的准备与经历分享 | 百度(offer)
Google、滴滴 与 Udacity 联合开发的 Android 课程,有来自硅谷的实战项目,并提供一对一代码审阅和技术辅导,现在部分课程能免费体验,感兴趣的朋友可以扫下面的二维码。